﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Net.Sockets;
using Conexion;
using System.IO;

namespace Manager
{
    class ClientController
    {
        private const string CMD_RAM = "01";
        private const string CMD_CPU = "02";
        private const string CMD_DISCO = "03";
        private const string CMD_SERV = "04";
        private const string CMD_ERROR = "05";
        private const string Formato = "000000000000000";
        private Thread[] clientThreads = new Thread[5];
        private Object mutex = null;

        public ClientController()
        {
        }

        public void IniciarServicio(Object mutex)
        {
            this.mutex = mutex;
            Thread th = new Thread(AtenderClientes);
            th.Start();
        }

        private void AtenderClientes()
        {
            int count = 0;
            foreach (ClientNode c in Manager.conf.Clientes)
            {
                clientThreads[count] = new Thread(EscucharClientes);
                clientThreads[count].Start(c);
                count++;
            }
        }

        private void EscucharClientes(object config)
        {
            try
            {
                ClientNode m = (ClientNode)config;
                TcpListener listener = ListenerTCP.obtenerTcpListener(m.Ip, m.Puerto);
                listener.Start();

                TcpClient agente;
                Console.WriteLine("Escuchando peticiones del cliente en el puerto: " + m.Puerto);

                while (true)
                {
                    agente = listener.AcceptTcpClient();
                    ThreadPool.QueueUserWorkItem(new WaitCallback(ProcesarPeticionCliente), agente);
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine("Error al escuchar los clientes: " + ex.Message);
            }
        }

        private void ProcesarPeticionCliente(Object agente)
        {
            TcpClient tcpClient = (TcpClient)agente;
            NetworkStream ns;
            byte[] datos = new byte[1024];
            string hdr = "";
            string cmd = "";
            string response = "";
            string cmdConsultado = "";

            ns = tcpClient.GetStream();
            ns.Read(datos, 0, 3);
            hdr = Encoding.UTF8.GetString(datos).Substring(0, 3);

            if (hdr.Equals("REQ"))
            {
                ns.Read(datos, 0, 2);
                cmd = Encoding.UTF8.GetString(datos).Substring(0, 2);
                switch (cmd)
                {
                    case CMD_RAM:
                        cmdConsultado = CMD_RAM;
                        break;
                    case CMD_DISCO:
                        cmdConsultado = CMD_DISCO;
                        break;
                    case CMD_CPU:
                        cmdConsultado = CMD_CPU;
                        break;
                    case CMD_SERV:
                        cmdConsultado = CMD_SERV;
                        break;
                    default:
                        Console.WriteLine("Default case");
                        break;
                }
                string resp = "";
                if (!cmdConsultado.Equals(CMD_SERV))
                {
                    ServerNode srv = null;
                    // si piden por cpu, disco o ram, obtengo el servidor y lo consulto
                    try
                    {
                        ns.Read(datos, 0, 4);
                        int id = int.Parse(Encoding.UTF8.GetString(datos).Substring(0, 4));
                         srv = ObtenerAgenteConsulta(id);
                         if (srv.Vivo)
                         {
                             Command srvCmd = new Command(srv, cmdConsultado);
                             new RequestAgent(mutex, srv).EnviarRequestAgente(srvCmd);
                             resp = CrearRespuesta(srvCmd);
                         }
                         else
                             resp = "RES" + CMD_ERROR;
                    }
                    catch (Exception) {
                       // conf.EliminarServidor(srv);
                        Console.WriteLine("El servidor " + srv.Nombre + " (" + srv.Ip + ") no responde");
                        srv.Vivo = false;
                    }
                }
                else
                {
                    //si piden cant de servidores les les paso la cantidad
                    resp = CrearRespuestaCantServ();
                }
                byte[] respBy = Encoding.UTF8.GetBytes(resp);
                ns.Write(respBy, 0, respBy.Length);
                ns.Flush();

            }
            else
            {
                //throw ex
            }
            byte[] datosRespuesta = Encoding.UTF8.GetBytes(response);
            StreamWriter str = new StreamWriter(tcpClient.GetStream());
            str.Write(response);
            str.Flush();
        }

        private string CrearRespuesta(Command srvCmd)
        {
            string respuesta = "RES";
            string cmd = srvCmd.Comando;
            ServerNode srvMonitor = srvCmd.Servidor;

            respuesta += cmd;
            switch (cmd)
            {
                case CMD_CPU:
                    {
                        respuesta += long.Parse(srvMonitor.UsoCpu).ToString(Formato);
                        break;
                    }
                case CMD_DISCO:
                    {
                        respuesta += long.Parse(srvMonitor.DiscoTotal).ToString(Formato);
                        respuesta += long.Parse(srvMonitor.DiscoLibre).ToString(Formato);
                        break;
                    }
                case CMD_RAM:
                    {
                        respuesta += long.Parse(srvMonitor.RamTotal).ToString(Formato);
                        respuesta += long.Parse(srvMonitor.RamLibre).ToString(Formato);
                        break;
                    }
                case CMD_SERV:
                    {
                        respuesta += Manager.conf.ObtenerCantServidores().ToString();
                        respuesta += ObtenerDatosServidores();
                        break;
                    }
                default:
                    break;
            }
            return respuesta;
        }

        private string CrearRespuestaCantServ()
        {
            string respuesta = "RES";
            respuesta += CMD_SERV;
            respuesta += Manager.conf.ObtenerCantServidores().ToString();
            respuesta += ObtenerDatosServidores();
            return respuesta;
        }

        private ServerNode ObtenerAgenteConsulta(int id)
        {
            foreach (ServerNode serv in Manager.conf.ObtenerServidores())
            {
                if (id.Equals(serv.Identificador))
                    return serv;
            }
            throw new NotImplementedException();
        }

        private string ObtenerDatosServidores()
        {
            string nombreParcial = "";
            foreach (ServerNode serv in Manager.conf.ObtenerServidores())
            {
                if (serv.Vivo)
                {
                    nombreParcial += serv.Identificador.ToString("0000");
                    nombreParcial += serv.Nombre.PadLeft(15, '0');
                }
            }
            return nombreParcial;
        }
    }
}
